package com.onlyxiahui.extend.query.hibernate.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.onlyxiahui.extend.query.hibernate.QueryWrapper;
import com.onlyxiahui.extend.query.hibernate.parameter.ValueOptionInfo;
import com.onlyxiahui.extend.query.hibernate.parameter.ValueOption;

/**
 * 
 * Date 2018-12-29 10:20:34<br>
 * Description
 * 
 * @author XiaHui<br>
 * @since 1.0.0
 */
public class QueryUtil {

	private static final Map<Class<?>, List<PropertyDescriptor>> LIST_MAP = new ConcurrentHashMap<Class<?>, List<PropertyDescriptor>>();
	private static final Map<Class<?>, Map<String, PropertyDescriptor>> KEY_MAP = new ConcurrentHashMap<Class<?>, Map<String, PropertyDescriptor>>();

	public static QueryWrapper getQueryWrapper(Object value) {
		return getQueryWrapper(value, null, null);
	}

	public static QueryWrapper getQueryWrapper(Object value, QueryWrapper queryWrapper) {
		return getQueryWrapper(value, queryWrapper, null);
	}

	public static QueryWrapper getQueryWrapper(Object value, List<String> nameList) {
		return getQueryWrapper(value, null, nameList);
	}

	public static QueryWrapper getQueryWrapper(Object value, QueryWrapper queryWrapper, List<String> nameList) {
		return getQueryWrapperType(value, queryWrapper, nameList, null);
	}

	public static QueryWrapper getQueryWrapperType(Object value, List<ValueOptionInfo> optionList) {
		return getQueryWrapperType(value, null, optionList);
	}

	public static QueryWrapper getQueryWrapperType(Object value, QueryWrapper queryWrapper, List<ValueOptionInfo> optionList) {
		return getQueryWrapperType(value, queryWrapper, null, optionList);
	}

	public static QueryWrapper getQueryWrapperType(Object value, QueryWrapper queryWrapper, List<String> nameList, List<ValueOptionInfo> optionList) {
		if (null == queryWrapper) {
			queryWrapper = new QueryWrapper();
		}

		try {
			if (null != value) {
				int size = null == optionList ? 0 : optionList.size();
				Map<String, ValueOptionInfo> optionMap = new HashMap<>(size);
				if (null != optionList && !optionList.isEmpty()) {
					for (ValueOptionInfo option : optionList) {
						optionMap.put(option.getName(), option);
					}
				}

				if (null == nameList || nameList.isEmpty()) {
					if (value instanceof Map) {
						Map<?, ?> map = (Map<?, ?>) value;
						Set<?> keySet = map.keySet();
						for (Object key : keySet) {
							Object data = map.get(key);
							String name = null == key ? "" : key.toString();
							ValueOptionInfo option = optionMap.get(name);
							ValueOption type = (null == option) ? null : option.getOptionType();
							if (null != data) {
								if (null != type) {
									queryWrapper.addParameter(name, type.getText(data));
								} else {
									queryWrapper.addParameter(name, data);
								}
							}
						}
					} else {
						List<PropertyDescriptor> propertyDescriptorList = getReadMethodList(value.getClass());
						for (PropertyDescriptor pd : propertyDescriptorList) {
							String propertyName = pd.getName();
							Method readMethod = pd.getReadMethod();
							Object data = readMethod.invoke(value);
							ValueOptionInfo option = optionMap.get(propertyName);
							ValueOption type = (null == option) ? null : option.getOptionType();
							if (null != data) {
								if (null != type) {
									queryWrapper.addParameter(propertyName, type.getText(data));
								} else {
									queryWrapper.addParameter(propertyName, data);
								}
							}
						}
					}
				} else {
					if (value instanceof Map) {
						Map<?, ?> map = (Map<?, ?>) value;
						for (String name : nameList) {
							Object data = map.get(name);
							ValueOptionInfo option = optionMap.get(name);
							ValueOption type = (null == option) ? null : option.getOptionType();
							if (null != data) {
								if (null != type) {
									queryWrapper.addParameter(name, type.getText(data));
								} else {
									queryWrapper.addParameter(name, data);
								}
							}
						}
					} else {
						Map<String, PropertyDescriptor> map = getReadMethodMap(value.getClass());
						for (String name : nameList) {
							PropertyDescriptor pd = map.get(name);
							if (null != pd) {
								String propertyName = pd.getName();
								Method readMethod = pd.getReadMethod();
								Object data = readMethod.invoke(value);
								ValueOptionInfo option = optionMap.get(propertyName);
								ValueOption type = (null == option) ? null : option.getOptionType();
								if (null != data) {
									if (null != type) {
										queryWrapper.addParameter(propertyName, type.getText(data));
									} else {
										queryWrapper.addParameter(propertyName, data);
									}
								}
							}
						}
					}
				}
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return queryWrapper;
	}

	public static List<PropertyDescriptor> getReadMethodList(Class<?> classType) {
		List<PropertyDescriptor> list = LIST_MAP.get(classType);
		try {
			if (null == list) {
				list = new ArrayList<>();
				BeanInfo bi = Introspector.getBeanInfo(classType, Object.class);
				PropertyDescriptor[] pds = bi.getPropertyDescriptors();
				for (PropertyDescriptor pd : pds) {
					if (null != pd.getReadMethod()) {
						list.add(pd);
					}
				}
				LIST_MAP.put(classType, list);
			}
		} catch (IntrospectionException e) {
			e.printStackTrace();
		}
		return list;
	}

	public static Map<String, PropertyDescriptor> getReadMethodMap(Class<?> classType) {
		Map<String, PropertyDescriptor> map = KEY_MAP.get(classType);
		try {
			if (null == map) {
				BeanInfo bi = Introspector.getBeanInfo(classType, Object.class);
				PropertyDescriptor[] pds = bi.getPropertyDescriptors();
				map = new HashMap<>(pds.length);
				for (PropertyDescriptor pd : pds) {
					if (null != pd.getReadMethod()) {
						map.put(pd.getName(), pd);
					}
				}
				KEY_MAP.put(classType, map);
			}
		} catch (IntrospectionException e) {
			e.printStackTrace();
		}
		return map;
	}
}
